#include "../include/commands.h"

extern qword iregs[128];
extern real4 fregs[32];
extern real8 dregs[32];
extern VREG vregs[64];
extern std::fstream disk;
extern Table gdt[4096];
extern byte ram[MAX_MEM];

void device(byte reg)
{
	if (reg >= 128)
	{
		// TODO:Register code error
	}
	word port = iregs[reg & 0x7f] & 0xffff;
	word op = (iregs[reg & 0x7f] >> 16) & 0xff;
	switch (op)
	{
	case 0x00:
		if (port == 0x00)
			mount();
		break;
	case 0x01:
		if (port == 0x00)
			umount();
		break;
	default: // TODO:Throw Error:Invalid Op Code
		break;
	}
}
void trap(command code)
{
	switch (code.imm1)
	{
	case 0x00:
		for (int i = 0; i < iregs[Count]; i++)
			*(ram + AddrTrans(iregs[DI] + i)) = getchar();
		break;
	case 0x01:
		for (int i = 0; i < iregs[Count]; i++)
			putchar(*(ram + AddrTrans(iregs[SI] + i)));
		break;
	case 0x02:
		file_read();
		break;
	case 0x03:
		file_write();
		break;
	case 0x04:
		device(code.t_reg);
	case 0x05:
		memcpy(ram + AddrTrans(iregs[DI]), ram + AddrTrans(iregs[SI]), iregs[Count]);
		break;
	default:
		// 0x08,0x20 need to be handled for assemble code
		if (iregs[Flag] & PROTECTED)
			iregs[Code] = iregs[IDTR] + (qword)(code.imm1) * 8;
		else
			iregs[Code] = (qword)(code.imm1) * 2;
		break;
	}
}

command loader()
{
	command result;
	result.c_value = *(ram + iregs[Code]);
	result.t_reg = *(ram + iregs[Code] + 1);
	result.s_reg1 = *(ram + iregs[Code] + 2);
	result.imm1 = *(ram + iregs[Code] + 3);
	result.s_reg2 = *(ram + iregs[Code] + 4);
	result.imm2 = *(ram + iregs[Code] + 5);
	result.imm3 = *(ram + iregs[Code] + 6);
	result.imm3 = (result.imm3 << 8) + *(ram + iregs[Code] + 7);
	iregs[Code] += 8;
	return result;
}
void selector(command code)
{
	switch (code.c_value >> 4)
	{
	case SPECIAL:
		spec(code);
		break;
	case CALC:
		calc(code);
		break;
	case LOG:
		logic(code);
		break;
	case MEMOP:
		memop(code);
		break;
		//	case AVXC:
		//		avx_calc(code);
		//		break;
		//	case AVXM:
		//		avx_mem(code);
		//		break;
	default:
		break;
	}
}
void spec(command code)
{
	switch (code.c_value & 0x0f)
	{
	case 0x00: // Trap command
		trap(code);
		break;
	case 0x01: // Push
		iregs[Stack] -= 8;
		if (code.t_reg < 128)
			memcpy(ram + AddrTrans(iregs[Stack]), iregs + code.t_reg, 8);
		else if (code.t_reg < 160)
			memcpy(ram + AddrTrans(iregs[Stack]), iregs + (code.t_reg & 0x1f), 4);
		else if (code.t_reg < 192)
			memcpy(ram + AddrTrans(iregs[Stack]), iregs + (code.t_reg & 0x1f), 8);
		else
		{
			// TODO:Register code error
		}
		break;
	case 0x02: // Pop
		if (code.t_reg < 128)
			memcpy(iregs + code.t_reg, ram + AddrTrans(iregs[Stack]), 8);
		else if (code.t_reg < 160)
			memcpy(fregs + (code.t_reg & 0x1f), ram + AddrTrans(iregs[Stack]), 4);
		else if (code.t_reg < 192)
			memcpy(dregs + (code.t_reg & 0x1f), ram + AddrTrans(iregs[Stack]), 8);
		else
		{
			// TODO:Register code error
		}
		break;
	case 0x03: // Move
		if ((code.t_reg < 128) && (code.s_reg1 < 128))
			iregs[code.t_reg] = iregs[code.s_reg1];
		else if ((code.t_reg < 160) && (code.s_reg1 < 160))
			fregs[(code.t_reg & 0x1f)] = fregs[code.s_reg1 & 0x1f];
		else if ((code.t_reg < 192) && (code.s_reg1 < 192))
			dregs[code.t_reg & 0x1f] = dregs[code.s_reg1 & 0x1f];
		else if ((code.t_reg < 256) && (code.s_reg1 < 256))
			vregs[code.t_reg & 0x3f] = vregs[code.s_reg1 & 0x3f];
		else
		{
			// TODO:Register code error
		}
		break;
	case 0x04: // Cast
		if (code.s_reg1 > 192)
		{
			// TODO:Register code error
		}
		switch (code.imm1)
		{
		case DTYPE::BYTE:
			if (code.t_reg > 128)
			{
				// TODO:Register code error
			}
			if (code.s_reg1 < 128)
				iregs[code.t_reg & 0x7f] = iregs[code.s_reg1] & 0xff;
			else
				iregs[code.t_reg & 0x7f] = code.s_reg1 < 160 ? (byte)(fregs[code.s_reg1 & 0x1f]) : (byte)(dregs[code.s_reg1 & 0x1f]);
			break;
		case DTYPE::WORD:
			if (code.t_reg > 128)
			{
				// TODO:Register code error
			}
			if (code.s_reg1 < 128)
				iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] & 0xffff;
			else
				iregs[code.t_reg & 0x7f] = code.s_reg1 < 160 ? (word)(fregs[code.s_reg1 & 0x1f]) : (word)(dregs[code.s_reg1 & 0x1f]);
			break;
		case DTYPE::DWORD:
			if (code.t_reg > 128)
			{
				// TODO:Register code error
			}
			if (code.s_reg1 < 128)
				iregs[code.t_reg & 0x7f] = iregs[code.s_reg1];
			else
				iregs[code.t_reg & 0x7f] = code.s_reg1 < 160 ? (dword)(fregs[code.s_reg1 & 0x1f]) : (dword)(dregs[code.s_reg1 & 0x1f]);
			break;
		case DTYPE::QWORD:
			if (code.t_reg > 128)
			{
				// TODO:Register code error
			}
			if (code.s_reg1 < 128)
				iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f];
			else
				iregs[code.t_reg & 0x7f] = code.s_reg1 < 160 ? (qword)(fregs[code.s_reg1 & 0x1f]) : (qword)(dregs[code.s_reg1 & 0x1f]);
			break;
		case DTYPE::REAL4:
			if ((code.t_reg < 128) || (code.t_reg > 160))
			{
				// TODO:Register code error
			}
			if (code.s_reg1 < 128)
				fregs[(code.t_reg & 0x1f)] = (real4)iregs[code.s_reg1];
			else if (code.s_reg1 < 160)
				fregs[(code.t_reg & 0x1f)] = fregs[code.s_reg1 & 0x1f];
			else
				fregs[(code.t_reg & 0x1f)] = (real4)(dregs[code.s_reg1 & 0x1f]);
			break;
		case DTYPE::REAL8:
			if (code.t_reg < 160)
			{
				// TODO:Register code error
			}
			if (code.s_reg1 < 128)
				dregs[code.t_reg & 0x1f] = (real8)iregs[code.s_reg1];
			else if (code.s_reg1 < 160)
				dregs[code.t_reg & 0x1f] = fregs[code.s_reg1 & 0x1f];
			else
				dregs[code.t_reg & 0x1f] = dregs[code.s_reg1 & 0x1f];
			break;
		}
		break;
	case 0x05:
		iregs[Flag] |= ((qword)1) << (code.imm1 & 0x3f);
		break;
	case 0x06:
		iregs[Flag] &= (0xffffffffffffffff - (((qword)1) << (code.imm1 & 0x3f)));
		break;
	case 0x07: // Command "callq",call a system interrupt
		memcpy(ram + AddrTrans(iregs[Stack] - 8), iregs + Stack, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 16), iregs + Base, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 24), iregs + TSSR, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 32), iregs + LDTR, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 40), iregs + Data, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 48), iregs + Code, 8);
		iregs[Stack] -= 48;
		if (iregs[Flag] & PROTECTED)
			memcpy(iregs + Code, ram + AddrTrans(iregs[LDTR] + code.imm3 * 8), 8);
		else
			memcpy(iregs + Code, ram + AddrTrans(code.imm3), 2);
	case 0x08: // Call
		iregs[Base] = iregs[Stack];
		if (iregs[Flag] & PROTECTED)
			memcpy(iregs + Code, ram + AddrTrans(iregs[LDTR] + code.imm1 * 8), 8);
		else
			memcpy(iregs + Code, ram + AddrTrans(code.imm3), 2);
		break;
	case 0x09: // Ret
		memcpy(iregs + Code, ram + AddrTrans(iregs[Base]), 8);
		memcpy(iregs + Data, ram + AddrTrans(iregs[Base] + 8), 8);
		memcpy(iregs + LDTR, ram + AddrTrans(iregs[Base] + 16), 8);
		memcpy(iregs + TSSR, ram + AddrTrans(iregs[Base] + 24), 8);
		memcpy(iregs + Stack, ram + AddrTrans(iregs[Base] + 32), 8);
		memcpy(iregs + Base, ram + AddrTrans(iregs[Base] + 40), 8);
		break;
	case 0x0a: // Precall
		memcpy(ram + AddrTrans(iregs[Stack] - 8), iregs + Stack, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 16), iregs + Base, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 24), iregs + TSSR, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 32), iregs + LDTR, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 40), iregs + Data, 8);
		memcpy(ram + AddrTrans(iregs[Stack] - 48), iregs + Code, 8);
		iregs[Stack] -= 48;
		break;
	case 0x0E: // lgdt
		memcpy(gdt, ram + AddrTrans(iregs[code.t_reg]), sizeof(Table) * (code.imm3 & 0xfff));
		break;
	case 0x0F: // sgdt
		memcpy(gdt + (code.imm3 & 0xfff), ram + AddrTrans(iregs[code.t_reg]), sizeof(Table));
		break;
	}
}
void calc(command code)
{
	switch (code.c_value & 0x0f)
	{
	case 0x00: // Add
		switch (code.imm1)
		{
		case DTYPE::BYTE:
		case DTYPE::WORD:
		case DTYPE::DWORD:
		case DTYPE::QWORD:
			iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] + iregs[code.s_reg2 & 0x7f];
			break;
		case DTYPE::REAL4:
			fregs[code.t_reg & 0x1f] = fregs[code.s_reg1 & 0x1f] + fregs[code.s_reg2 & 0x1f];
			break;
		case DTYPE::REAL8:
			dregs[code.t_reg & 0x1f] = dregs[code.s_reg1 & 0x1f] + dregs[code.s_reg2 & 0x1f];
			break;
		}
		break;
	case 0x01:
		switch (code.imm1)
		{
		case DTYPE::BYTE:
		case DTYPE::WORD:
		case DTYPE::DWORD:
		case DTYPE::QWORD:
			iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] - iregs[code.s_reg2 & 0x7f];
			break;
		case DTYPE::REAL4:
			fregs[code.t_reg & 0x1f] = fregs[code.s_reg1 & 0x1f] - fregs[code.s_reg2 & 0x1f];
			break;
		case DTYPE::REAL8:
			dregs[code.t_reg & 0x1f] = dregs[code.s_reg1 & 0x1f] - dregs[code.s_reg2 & 0x1f];
			break;
		}
		break;
	case 0x02:
		switch (code.imm1)
		{
		case DTYPE::BYTE:
		case DTYPE::WORD:
		case DTYPE::DWORD:
		case DTYPE::QWORD:
			iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] * iregs[code.s_reg2 & 0x7f];
			break;
		case DTYPE::REAL4:
			fregs[code.t_reg & 0x1f] = fregs[code.s_reg1 & 0x1f] * fregs[code.s_reg2 & 0x1f];
			break;
		case DTYPE::REAL8:
			dregs[code.t_reg & 0x1f] = dregs[code.s_reg1 & 0x1f] * dregs[code.s_reg2 & 0x1f];
			break;
		}
		break;
	case 0x03:
		switch (code.imm1)
		{
		case DTYPE::BYTE:
		case DTYPE::WORD:
		case DTYPE::DWORD:
		case DTYPE::QWORD:
			iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] / iregs[code.s_reg2 & 0x7f];
			break;
		case DTYPE::REAL4:
			fregs[code.t_reg & 0x1f] = fregs[code.s_reg1 & 0x1f] / fregs[code.s_reg2 & 0x1f];
			break;
		case DTYPE::REAL8:
			dregs[code.t_reg & 0x1f] = dregs[code.s_reg1 & 0x1f] / dregs[code.s_reg2 & 0x1f];
			break;
		}
		break;
	case 0x05:
		iregs[code.t_reg & 0x7f] += 1;
		break;
	case 0x06:
		iregs[code.t_reg & 0x7f] -= 1;
		break;
	case 0x07:
		iregs[Flag] &= ~POS;
		iregs[Flag] &= ~NEG;
		iregs[Flag] &= ~ZERO;
		if (code.imm1 == DTYPE::QWORD)
		{
			if (iregs[code.s_reg1 & 0x7f] == iregs[code.s_reg2 & 0x7f])
				iregs[Flag] |= ZERO;
			else if ((long long)iregs[code.s_reg1 & 0x7f] < (long long)iregs[code.s_reg2 & 0x7f])
				iregs[Flag] |= NEG;
			else
				iregs[Flag] |= POS;
		}
		else if (code.imm1 == DTYPE::REAL4)
		{
			if (fabsf(fregs[code.s_reg1 & 0x1f] - fregs[code.s_reg2 & 0x1f]) < 1e-64)
				iregs[Flag] |= ZERO;
			else if (fregs[code.s_reg1 & 0x1f] < fregs[code.s_reg2 & 0x1f])
				iregs[Flag] |= NEG;
			else
				iregs[Flag] |= POS;
		}
		else if (code.imm1 == DTYPE::REAL8)
		{
			if (fabs(dregs[code.s_reg1 & 0x1f] - dregs[code.s_reg2 & 0x1f]) < 1e-64)
				iregs[Flag] |= ZERO;
			else if (dregs[code.s_reg1 & 0x1f] < dregs[code.s_reg2 & 0x1f])
				iregs[Flag] |= NEG;
			else
				iregs[Flag] |= POS;
		}
		break;
	case 0x08:
		iregs[Flag] &= ~POS;
		iregs[Flag] &= ~NEG;
		iregs[Flag] &= ~ZERO;
		if (code.imm1 == DTYPE::QWORD)
		{
			if (iregs[code.t_reg & 0x7f] == 0)
				iregs[Flag] |= ZERO;
			else if ((long long)iregs[code.s_reg1 & 0x7f] < 0)
				iregs[Flag] |= NEG;
			else
				iregs[Flag] |= POS;
		}
		else if (code.imm1 == DTYPE::REAL4)
		{
			if (fabsf(fregs[code.t_reg & 0x1f]) < 1e-64)
				iregs[Flag] |= ZERO;
			else if (fregs[code.t_reg & 0x1f] < 0)
				iregs[Flag] |= NEG;
			else
				iregs[Flag] |= POS;
		}
		else if (code.imm1 == DTYPE::REAL8)
		{
			if (fabs(dregs[code.t_reg & 0x1f]) < 1e-64)
				iregs[Flag] |= ZERO;
			else if (dregs[code.t_reg & 0x1f] < 0)
				iregs[Flag] |= NEG;
			else
				iregs[Flag] |= POS;
		}
		break;
	}
}
void logic(command code)
{
	switch (code.c_value & 0x0f)
	{
	case 0x00: // And
		iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] & iregs[code.s_reg2 & 0x7f];
		break;
	case 0x01: // Or
		iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] | iregs[code.s_reg2 & 0x7f];
		break;
	case 0x02: // Xor
		iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] ^ iregs[code.s_reg2 & 0x7f];
		break;
	case 0x03: // Not
		iregs[code.t_reg & 0x7f] = ~iregs[code.s_reg1 & 0x7f];
		break;
	case 0x04: // Shl
		iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] << (iregs[code.s_reg2 & 0x7f] & 0x3f);
		break;
	case 0x05: // Shr
		iregs[code.t_reg & 0x7f] = iregs[code.s_reg1 & 0x7f] >> (iregs[code.s_reg2 & 0x7f] & 0x3f);
		break;
	}
}
void memop(command code)
{
	size_t len = (size_t)1;
	union {
		qword qvalue;
		dword dvalue;
		word wvalue;
		byte bvalue;
	}temp;
	switch (code.c_value & 0x0f)
	{
	case 0x00:
		switch (code.imm1)
		{
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
			len = static_cast<size_t>(1) << code.imm1;
			memcpy(iregs + (code.t_reg & 0x7f), ram + AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3), len);
			break;
		case 0x04:
			len = sizeof(real4);
			memcpy(fregs + (code.t_reg & 0x1f), ram + AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3), len);
			break;
		case 0x05:
			len = sizeof(real8);
			memcpy(dregs + (code.t_reg & 0x1f), ram + AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3), len);
			break;
		}
		break;
	case 0x01:
		switch (code.imm1)
		{
		case 0x00:
			len = 1;
			temp.bvalue = iregs[code.s_reg1 & 0x7f] & 0xff;
			memcpy(ram + AddrTrans(iregs[code.t_reg & 0x7f] + code.imm3), &(temp.bvalue), len);
			break;
		case 0x01:
			len = 2;
			temp.wvalue = iregs[code.s_reg1 & 0x7f] & 0xffff;
			memcpy(ram + AddrTrans(iregs[code.t_reg & 0x7f] + code.imm3), &(temp.wvalue), len);
			break;
		case 0x02:
			len = 4;
			temp.dvalue = iregs[code.s_reg1 & 0x7f] & 0xffffffff;
			memcpy(ram + AddrTrans(iregs[code.t_reg & 0x7f] + code.imm3), &(temp.dvalue), len);
			break;
		case 0x03:
			len = 8;
			memcpy(ram + AddrTrans(iregs[code.t_reg & 0x7f] + code.imm3), iregs + (code.s_reg1 & 0x7f), len);
			break;
		case 0x04:
			len = sizeof(real4);
			memcpy(ram + AddrTrans(iregs[code.t_reg & 0x7f] + code.imm3), fregs + (code.s_reg1 & 0x1f), len);
			break;
		case 0x05:
			len = sizeof(real8);
			memcpy(ram + AddrTrans(iregs[code.t_reg & 0x7f] + code.imm3), dregs + (code.s_reg1 & 0x1f), len);
			break;
		}
		break;
	case 0x02:
		switch (code.imm1)
		{
		case NONE:
			break;
		case EQU:
			if ((long long)iregs[code.s_reg2 & 0x7f] == 0)
				iregs[Code] = AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3);
			break;
		case LT:
			if ((long long)iregs[code.s_reg2 & 0x7f] < 0)
				iregs[Code] = AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3);
			break;
		case LE:
			if ((long long)iregs[code.s_reg2 & 0x7f] <= 0)
				iregs[Code] = AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3);
			break;
		case GT:
			if ((long long)iregs[code.s_reg2 & 0x7f] > 0)
				iregs[Code] = AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3);
			break;
		case GE:
			if ((long long)iregs[code.s_reg2 & 0x7f] >= 0)
				iregs[Code] = AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3);
			break;
		case NEQ:
			if ((long long)iregs[code.s_reg2 & 0x7f] != 0)
				iregs[Code] = AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3);
			break;
		case ALL:
			iregs[Code] = AddrTrans(iregs[code.s_reg1 & 0x7f] + code.imm3);
			break;
		}
		break;
	}
}
void avx_calc(command code);
void avx_mem(command code);